package org.fjsei.yewu.graphql.directive;

import graphql.Scalars;
import graphql.language.FloatValue;
import graphql.schema.*;
import graphql.schema.idl.SchemaDirectiveWiring;
import graphql.schema.idl.SchemaDirectiveWiringEnvironment;
import org.fjsei.yewu.graphql.MyDataFetcherFactories;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

/**graphQL接口层次： 自动把字符串转成LocalDate
 * 代码来源：  https://www.graphql-java.com/documentation/sdl-directives/
 * 对应的注解是 directive @dateFormat on FIELD_DEFINITION  模型文件定义带上。
 * 模型定义文件上： @dateFormat 改为 @date
 * type Query {  dateField : String @dateFormat "
 * }
 * 客户端的查询语句实际是：
 * query {  default : dateField
            usa : dateField(format : \"MM-dd-YYYY\")
 *       }
 * 稀奇了# 竟然可直接把属性后面直接当做接口函数附加(format : \"MM-dd-YYYY\") 当做一个属性的参数列表的输入参数就传递给了@dateFormat注解解析器了，二传手。
 * */
public class DateFormatting implements SchemaDirectiveWiring {
    /** 把LocalDateTime的属性按预订格式输出；
     * */
    @Override
    public GraphQLFieldDefinition onField(SchemaDirectiveWiringEnvironment<GraphQLFieldDefinition> environment) {
        GraphQLFieldDefinition field = environment.getElement();
        GraphQLFieldsContainer parentType = environment.getFieldsContainer();
        //
        // DataFetcherFactories.wrapDataFetcher is a helper to wrap data fetchers so that CompletionStage is handled correctly
        // along with POJOs
        //
        DataFetcher originalFetcher = environment.getCodeRegistry().getDataFetcher(parentType, field);
        DataFetcher dataFetcher = DataFetcherFactories.wrapDataFetcher(originalFetcher, ((dataFetchingEnvironment, value) -> {
            //假如这个属性是返回内省情况：实际是在获得属性字段数据以后的最后一步进行转换的。
            DateTimeFormatter dateTimeFormatter = buildFormatter(dataFetchingEnvironment.getArgument("format"));
            if (value instanceof LocalDateTime) {
                return dateTimeFormatter.format((LocalDateTime) value);
            }
            return value;
        }));

        //
        // This will extend the field by adding a new "format" argument to it for the date formatting
        // which allows clients to opt into that as well as wrapping the base data fetcher so it
        // performs the formatting over top of the base values.
        //
        FieldCoordinates coordinates = FieldCoordinates.coordinates(parentType, field);
        environment.getCodeRegistry().dataFetcher(coordinates, dataFetcher);

        //底下的defaultValueProgrammatic("dd-MM-YYYY") 注解的默认的format参数
        return field.transform(builder -> builder
                .argument(GraphQLArgument
                        .newArgument()
                        .name("format")
                        .type(Scalars.GraphQLString)
                        .defaultValueProgrammatic("yyyy-MM-dd")
                )
        );
    }

    private DateTimeFormatter buildFormatter(String format) {
        //日期格式 ？？dd-MM-YYYY
        String dtFormat = format != null ? format : "yyyy-MM-dd";
        return DateTimeFormatter.ofPattern(dtFormat);
    }
    /**把输入的预定义前端输入的json字符串格式日期 直接转换成java LocalDate类型。
     * */
    @Override
    public GraphQLArgument onArgument(SchemaDirectiveWiringEnvironment<GraphQLArgument> environment) {
        GraphQLArgument graphQLArgument =environment.getElement();
        //针对模型接口方法的参数注解；在模型文件规定好了，如@range(min: 1.0, max: 999.0)属性取值。
     //   FloatValue minObj = (FloatValue) environment.getDirective().getArgument("min").getArgumentValue().getValue();
     //   double min = null != minObj ? minObj.getValue().doubleValue() : 0;      //graphql.language.FloatValue cannot be cast to class java.lang.Double

        DataFetcher originalFetcher = environment.getFieldDataFetcher();
        //利用MyDataFetcherFactories工具再次套个 逻辑转换：
        DataFetcher dataFetcher = MyDataFetcherFactories
                .beforeDataFetcher(graphQLArgument.getName(), originalFetcher, ((dataFetchingEnvironment, value) -> {
                    //[回调钩子]涉及@range注解的情形；模型接口函数每次实际操作前，都要检查参数。
                    if(! (value instanceof String) ) {
                        throw new IllegalArgumentException(
                                String.format("参数检查失败 %s", value)
                        );
                    }
                    //【！问题！】直接替换掉了原设定的返回给前端的属性取值   ！奇怪了？ 输入参数的为何影响到输出。
                    LocalDate valDate=LocalDate.parse((CharSequence) value, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
                    return valDate;
                }));
        //每个使用@range注解的参数都会登记这里一次，初始化schema时刻搞。
        environment.getCodeRegistry().dataFetcher(
                environment.getFieldsContainer(),
                environment.getFieldDefinition(),
                dataFetcher
        );
        //试试
        graphQLArgument.transform(builder -> builder
                .clearDefaultValue().value("dfsff")
                );

        return environment.getElement();
    }
}

